#include "Soup.h"
#include <GL/gl.h>
#include <algorithm>
#include <string>
#include <fstream>
#include <iostream>
#include "ParserOBJ.h"
#include "Error.h"

/**
  @author F. Aubert
  TODO : !!! libération mémoire sur delete
*/
using namespace std;
using namespace prog3d;



Soup::Soup() {
	drawMode=DRAW_FILL;
	_currentGroup=NULL;
	_baseMaterial=new BaseMaterial;
}

void Soup::enableGroup(const string &nom) {
  ObjGroup *o=findGroup(nom);
  if (o) o->enable();
}

void Soup::disableGroup(const string &nom) {
  ObjGroup *o=findGroup(nom);
  if (o) o->disable();
}


void Soup::enableAllGroup() {
  for(unsigned int i=0;i<_group.size();i++) {
    _group[i]->enable();
  }
}

void Soup::disableAllGroup() {
  for(unsigned int i=0;i<_group.size();i++) {
    _group[i]->disable();
  }
}

Vector3 *Soup::createTexCoord(const Vector3 &t) {
  Vector3 *res;
  res=new Vector3(t);
  vt.push_back(res);
  return res;
}

Vector3 *Soup::getTexCoord(unsigned int i) {
  return vt[i];
}

Soup::~Soup() {
  // TODO : à refaire pour memory leak
//    cout << "end clearing" << endl;
}

void Soup::addGroup(ObjGroup *o) {
  _group.addEnd(o);
}

void Soup::eraseFacet(Facet *f) {
	AFacet::iterator i;
	i=find(vf.begin(),vf.end(),f);
	eraseFacet(i);
}

void Soup::eraseFacet(AFacet::iterator i) {
	(*i)->clearVVertex();
	(*i)->clearVNormal();
	delete (*i);
	vf.erase(i);
}

void Soup::eraseFacet(int i) {
	eraseFacet(vf.begin()+i);
}


void Soup::triangulate() {
    int fin=vf.size();
    for(int i=0;i<fin;i++) {
        vf[i]->triangulate();
    }
}


Vertex *Soup::createVertex() {
	Vertex *res=new Vertex(this);
	addVertex(res);
	return res;
}

Vertex *Soup::createVertex(const Vector3 &p) {
	Vertex *res=createVertex();
	res->point(p);
	return res;
}

Facet *Soup::createFacet() {
	Facet *res=new Facet(this);
	addFacet(res);
	if (_currentGroup) {
//	  cout << "add to current group " << _currentGroup->name() << endl;
    _currentGroup->addFacet(res);
	}
	return res;
}


void Soup::setDrawMode(unsigned int mode) {
	drawMode|=mode;
}

/*
void Soup::draw() {
  vf.setDrawMode(drawMode);
  vf.draw();
}
*/

void Soup::draw() {
  if (drawMode & DRAW_MTL) {
  AObjGroup::iterator itGroup;
//  cout << "nbre _group" << component.size() << endl;
  for(itGroup=_group.begin();itGroup!=_group.end();itGroup++) {
    (*itGroup)->draw(drawMode);
  }
  }
  else {
  vf.setDrawMode(drawMode);
  vf.draw();
  }
}


const AFacet &Soup::getAllFacet() const {
    return vf;
}

const AVertex &Soup::getAllVertex() const {
    return vv;
}

Facet *Soup::getFacet(unsigned int i) {
	return vf[i];
}


void Soup::setNumber() {
    int index=0;
    for(AVertex::iterator i=vv.begin();i!=vv.end();i++) {
        (*i)->setNumber(index);
        index++;
    }
}

Vertex *Soup::getVertex(int i) {
    return vv[i];
}

Vector3 *Soup::getNormal(int i) {
    return vn[i];
}

void Soup::addVertex(Vertex *v) {
    vv.push_back(v);
}

void Soup::addNormal(Vector3 *n) {
    vn.push_back(n);
}

void Soup::addFacet(Facet *f) {
    vf.push_back(f);
}

unsigned int Soup::nbFacet() {
	return vf.size();
}


void Soup::dilate(float k) {
  AVertex::iterator ita;
  for(ita=vv.begin();ita!=vv.end();ita++) {
    Vector3 p=(*ita)->normal();
    p.scaleAdd(k,(*ita)->point());
    (*ita)->point(p);
  }
}


bool minx(Vertex *p1, Vertex *p2) {
  return p1->point().x()<p2->point().x();
}

bool miny(Vertex *p1, Vertex *p2) {
  return p1->point().y()<p2->point().y();
}

bool minz(Vertex *p1, Vertex *p2) {
  return p1->point().z()<p2->point().z();
}

typedef bool (*fct_min)(Vertex *p1,Vertex *p2);

fct_min mini_fct[3]={minx,miny,minz};


double Soup::minBox(int coord) {
  AVertex::iterator ita;
  ita=min_element(vv.begin(),vv.end(),mini_fct[coord]);
  if (ita==vv.end()) return 0.0; else
  return (*ita)->point().get(coord);
}

double Soup::maxBox(int coord) {
  AVertex::iterator ita;
  ita=max_element(vv.begin(),vv.end(),mini_fct[coord]);
  if (ita==vv.end()) return 0.0; else
  return (*ita)->point().get(coord);
}

void Soup::adaptBox(float x1,float x2,float y1,float y2, float z1, float z2) {
  float min_x=minBox(0);
  float max_x=maxBox(0);
  float min_y=minBox(1);
  float max_y=maxBox(1);
  float min_z=minBox(2);
  float max_z=maxBox(2);

  int the_maxi=0;

  if ((max_x-min_x>=max_y-min_y) && (max_x-min_x>=max_z-min_z)) {
    the_maxi=0;
  } else {
    if ((max_y-min_y>=max_x-min_x) && (max_y-min_y>=max_z-min_z)) {
      the_maxi=1;
    } else {
      the_maxi=2;
    }
  }

  this->translate(-(min_x+max_x)/2.0,-(min_y+max_y)/2.0,-(min_z+max_z)/2.0);
  if (the_maxi==0) {
    float kscale=(x2-x1)/(max_x-min_x);
    this->scale(kscale);
  } else {
    if (the_maxi==1) {
      float kscale=(y2-y1)/(max_y-min_y);
      this->scale(kscale);
    } else {
      float kscale=(z2-z1)/(max_z-min_z);
      this->scale(kscale);
    }
  }
  this->translate((x1+x2)/2.0,(y1+y2)/2.0,(z1+z2)/2.0);
}

void Soup::drawNormal() {
  AVertex::iterator ita;
  Vector3 pp;
  for(ita=vv.begin();ita!=vv.end();ita++) {
    glBegin(GL_LINES);
      glVertex3dv((*ita)->point().dv());
      pp.set((*ita)->normal());
      pp.scaleAdd(0.1,(*ita)->point());
      glVertex3dv(pp.dv());
    glEnd();
  }
}

void Soup::scale(float k) {
  AVertex::iterator itv;
  for(itv=vv.begin();itv<vv.end();itv++) {
    (*itv)->scale(k);
  }
}

void Soup::scale(double kx,double ky,double kz) {
  AVertex::iterator itv;
  for(itv=vv.begin();itv<vv.end();itv++) {
    (*itv)->scale(kx,ky,kz);
  }
}

void Soup::translate(float x,float y, float z) {
  AVertex::iterator itv;
  for(itv=vv.begin();itv<vv.end();itv++) {
    (*itv)->translate(x,y,z);
  }
}

void Soup::writeOBJ(string nom) {
    fstream f;
    f.open(nom.c_str(),fstream::out);
    AVertex::iterator itv;
    f << "#vertices : " << endl;

    int compte=0;
    for(itv=vv.begin();itv!=vv.end();itv++) {
        (*itv)->setNumber(compte);
        Vector3 p=(*itv)->point();
        f << "v " << p.x() << " " << p.y() << " " << p.z() << endl;
        compte++;
    }
    f << "# number of vertices " << compte << endl;

    f << endl;
    f << "#facets" << endl;
    AFacet::iterator itf;
    int comptef=0;
    for(itf=vf.begin();itf!=vf.end();itf++) {
        Facet *a=*itf;
        AVertex::iterator itv;
        f << "f ";
        for (itv=a->vertexBegin();itv!=a->vertexEnd();itv++) {
            f << (*itv)->getNumber()+1 << " ";
        }
        f << endl;
        comptef++;
    }
    f << "# number of facets :" << comptef << endl;
    f.close();
}

void Soup::readOBJ(string nom) {
    ParserOBJ parse(nom);
    if (!parse.isOk()) error(nom+" not found",__LINE__,__FILE__); else
    parse.load(this);
}

void Soup::computeVertexNormal() {
  AFacet::iterator itf;
  AVertex::iterator itv;
  vf.computeNormal();
  for(itv=vv.begin();itv!=vv.end();itv++) {
    (*itv)->averageNormalBegin();
  }
   for(itf=vf.begin();itf!=vf.end();itf++) {
     for(itv=(*itf)->vertexBegin();itv!=(*itf)->vertexEnd();itv++) {
       (*itv)->averageNormalAdd((*itf)->normal());
     }
   }
   for(itv=vv.begin();itv!=vv.end();itv++) {
//  printf("%f,%f,%f\n",(*ita)->n.x,(*ita)->n.y,(*ita)->n.z);
      (*itv)->averageNormalEnd();
   }
}

void Soup::computeFaceNormal() {
  vf.computeNormal();
}

void Soup::opposeNormal() {
	for(unsigned int i=0;i<vv.size();i++) {
		vv[i]->normal(-(vv[i]->normal()));
	}
}

void Soup::print(const string &mesg) {
	cout << "Objet : " << mesg << endl;
	for(unsigned int i=0;i<nbFacet();i++) {
		Facet *f=getFacet(i);
		cout << "face = (";
		for(unsigned int j=0;j<f->nbVertex();j++) {
			cout << f->getVertex(j )->point();
		}
		cout << ")" << endl;
	}
}


void Soup::setCurrentGroup(ObjGroup *o) {
  _currentGroup=o;
}

BaseMaterial *Soup::baseMaterial() {
  return _baseMaterial;
}

ObjGroup *Soup::findGroup(string s) {
  AObjGroup::iterator it;
  for(it=_group.begin();it!=_group.end();it++) {
    if ((*it)->name()==s) return (*it);
  }
  return NULL;
}

void Soup::buildEdge() {

}

void Soup::eraseDuplicateVertex() {
  for(unsigned int i=0;i<nbFace();i++) {
    vf[i]->eraseDuplicateVertex();
  }
}

unsigned int Soup::nbFace() {
  return vf.size();
}

unsigned int Soup::nbVertex() {
  return vv.size();
}


const Vector3 &Soup::point(unsigned int i) {
  return vv[i]->point();
}

const Vector3 &Soup::normal(unsigned int i) {
  return vv[i]->normal();
}

Facet *Soup::face(unsigned int i) {
  return vf[i];
}

Vertex *Soup::vertex(unsigned int i) {
  return vv[i];
}

void Soup::printGroup() {
  for(unsigned int i=0;i<_group.size();i++) {
    cout << "Group : " << _group[i]->name() << endl;
  }
}

